#include <Ape/CLI/ArgumentsParser.h>
#include <Ape/IO/Console.h>
#include <Ape/Collections/StringList.h>
#include <Ape/EIndexOutOfRange.h>

namespace Ape {
	namespace CLI {
		ArgumentsParser::ArgumentsParser (int argc, char** argv)
				: m_Tokens (argc, L"")
				, m_Executable (L"")
		{
			for (int i = 0; i < argc; i++) {
				m_Tokens[i] = argv[i];
			}
			Process();
		}
		ArgumentsParser::ArgumentsParser (const Array<String>& args)
				: m_Tokens (args.get_Size(), L"")
				, m_Executable (L"")
		{
			for (size_t i = 0; i < args.get_Size(); i++) {
				m_Tokens[i] = args[i];
			}
			Process();
		}
		ArgumentsParser::~ArgumentsParser() {}
		
		void ArgumentsParser::ProcessToken(const String& token) {
			size_t tokenSize = token.get_Length();
			if (tokenSize < 2)
				throw Ape::EInvalidArgument(L"Token's length is less than 2: " + token);
			
			wchar_t ch0 = token.WcharAt(0);
			wchar_t ch1 = token.WcharAt(1);
			int argStartsFrom = 0;
			if (ch0 == L'-' && ch1 != L'-') { // a single-letter arg: -h, -a, -d=/tmp
				argStartsFrom = 1;
			}
			else if (ch0 == L'-' && ch1 == L'-') { // a "long" arg: --help, --all, --directory=/tmp
				argStartsFrom = 2;
			}
			else {
				argStartsFrom = 0;
			}
			Ape::Collections::StringList pair;
			token.Substring(argStartsFrom).Split(L'=', pair, 2);
			String argName = pair.FetchFirst();
			String argValue = pair.get_Count() ? pair.FetchFirst() : L"";
			
			m_Options[argName] = argValue;
		}
		
		void ArgumentsParser::Process() {
			m_Executable = m_Tokens[0];
			for (size_t i = 1; i < m_Tokens.get_Size(); i++) {
				ProcessToken(m_Tokens[i]);
			}
		}
		
		const String ArgumentsParser::get_Executable() const {
			return m_Executable;
		}
		
		bool ArgumentsParser::IsSet (const String& optionName) {
			return m_Options.KeyExists(optionName);
		}
		String ArgumentsParser::StringValue (const String& optionName, bool mayBeClear, const String& defaultsTo) {
			if (IsSet(optionName)) {
				return m_Options[optionName];
			}
			if (mayBeClear) {
				return defaultsTo;
			}
			throw Ape::EIndexOutOfRange(L"Required option is not set: " + optionName);
		}
		void ArgumentsParser::_Dump (bool verbose) const {
			Object::_Dump (verbose);
			
			DUMP_NON_V_TYPE;
			
			using Ape::IO::StdOut;
			using Ape::IO::NewLine;
			
			StdOut
				<< L"\t\tTokens:" << NewLine;
			foreach (token, Array<String>, m_Tokens) {
				StdOut << L"\t\t\t" << (String) token << NewLine;
			}
			StdOut
				<< L"\t\tExecutable: '" << get_Executable() << L"'" << NewLine;
			
		}
	}
}
